module ActsAsDynamic
  module ClassMethods
  
    Filter = Struct.new(:name, :label, :conditions, :method, :block)
    ComboFilter = Struct.new(:name, :options, :block)
  
    class DynamicGenerator
      attr_accessor :filters, :combo_filters, :search_fields, :actions, :buttons
      
      def initialize
        @filters = []
        @combo_filters = []
        @search_fields = []
        @actions = []
        @buttons = []
      end
      
      # Existen distintas formas de especificar los filtros. Difieren en la 
      # forma o a quien delegan la evaluación de incluir o no los registros en 
      # los resultados
      # 
      # Utilizar una condition sql directamente
      #   filter :turno_tarde, "turno = 'tarde'"
      #   filter :activo,      ["activo = ?", true]
      #   
      # Utilizar una condition sql en el hash de opciones.
      #   filter :turno_tarde, :conditions => {:turno => 'tarde'}
      #   
      # Utilizar un bloque.
      #   filter(:turno_tarde) {|e| e.turno_tarde? }
      #   filter :turno_tarde, &:turno_tarde?
      #   
      # Utilizar un método del modelo.
      #   filter :turno_tarde, :turno_tarde?
      #   
      # Utilizar un método del controller para evaluar si se incluye el registro
      #   filter :turno_tarde, :method => :turno_tarde?
      # 
      def filter(*args, &block)
        options = args.last.is_a?(Hash) ? args.pop : {}
        name = args.shift
        param = args.shift
        case param
          when String
            conditions = param
          when Symbol
            block = param.to_proc
          else
            conditions = options[:conditions]
            method = options[:method].to_sym if !block && options[:method]
        end
        !args.empty? ? args.shift : options[:conditions]
        label = options[:label] || name.to_s.humanize
        label = 'Mostrar todo' if name == :all && !options[:label]
        @filters << Filter.new(name, label, conditions, method, block)
      end
      
      def combo_filter(nombre, options = {}, &block)
        @combo_filters << ComboFilter.new(nombre, options, block)
      end
      
      def search(*args)
        @search_fields = args
      end

      def action(name, options = {}, &block)
        label = options[:label] || name.to_s.humanize
        @actions << [name, label, options, block]
      end

      def button(options)
        @buttons << options
      end
    end

    # opciones: 
    #   :model => :nombre   --> Indica el modelo con el que trabajará el controller.
    #   :order => :campo    --> Orden por defecto de los registros en la lista.
    def acts_as_dynamic(options = {})
      set_model options
      @options = options.dup
      include ActsAsDynamic::SimpleNavigator::InstanceMethods
      layout '../../vendor/plugins/acts_as_dynamic/files/app/views/layouts/base'
      before_filter :get_parent if options[:parent]
      before_filter :get_current, :only => [:show, :edit, :update, :destroy]
      before_filter :get_list, :only => [:index]
      @@left_tabs = nil
      @@right_tabs = nil
      
      @generator = DynamicGenerator.new
      yield @generator if block_given?
    end
    
    def layout_dynamic
      layout '../../vendor/plugins/acts_as_dynamic/files/app/views/layouts/base'
    end

    def options
      @options 
    end
   
    def filters
      @generator.filters
    end
    
    def get_filter(name)
      @generator.filters.find {|f| f.name == name.to_sym} if name && !name.to_s.empty?
    end
    
    def combo_filters
      @generator.combo_filters
    end
    
    def search_fields
      @generator.search_fields
    end
    
    def buttons
      @generator.buttons
    end
    
    def actions
      @generator.actions
    end
    
    def tabs(*recursos)
      @@right_tabs = recursos.last.is_a?(Array) ? recursos.pop : []
      @@left_tabs  = [recursos].flatten
    end
    
    def left_tabs; @@left_tabs rescue nil; end
    def right_tabs; @@right_tabs rescue nil; end
    
    private
    
      # Extraer nombre del modelo con el que trabajará el controller.
      # Si no está especificado lo infiere.
      def set_model(options)
        @model_name = if options[:model]
           options[:model]
        else
          self.to_s.underscore.gsub(/_controller$/, "").singularize.to_sym 
        end
        @model_class  = Object::const_get(@model_name.to_s.classify)
        if parent = options[:parent]
          @parent_class = Object::const_get(parent.to_s.classify)
        end
      end
  end
end
  
ActionController::Base.extend ActsAsDynamic::ClassMethods
